﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Ndo.Maths
{
    public enum SignTypes
    {                            //NINZ
        Zero = 0x0,              //0000
        Positive = 0x1,          //0001
        Negative = 0x3,          //0011
        PositiveInfinity = 0x5,  //0101
        NegativeInfinity = 0x7,  //0111
        NaN              = 0x9,  //1001
    }
    public abstract class Real<TFinal> : Number<TFinal>, IRelational<TFinal>, IComparable<TFinal>, IEquatable<TFinal>, ISign
        where TFinal: Real<TFinal>, new()
    {
        protected Real()
        {}

        public virtual bool IsNaN
        { get { return false;} }
        public virtual bool IsPositiveInfinity
        { get { return false;} }
        public virtual bool IsNegativeInfinity
        { get { return false;} }
        public virtual  bool IsZero
        { get { return Equals(Zero); } }
        public virtual bool IsInfinity
        { get { return IsPositiveInfinity || IsPositiveInfinity; } }
        public virtual bool IsPositive
        { get { return GreaterThan(GetNegation()); } }
        public virtual bool IsNegative
        { get { return LessThan(GetNegation()); } }



        #region IRelational<TFinal> Members

        public virtual bool LessThan(TFinal other)
        {
            return OperatorDelegates<TFinal>.LessThan(Self, other);
        }

        public virtual bool LessThanOrEqual(TFinal other)
        {
            return LessThan(other) || Equals(other);
        }

        public virtual bool GreaterThan(TFinal other)
        {
            return !(LessThan(other) || Equals(other));
        }

        public virtual bool GreaterThanOrEqual(TFinal other)
        {
            return !LessThan(other);
        }

        public virtual bool Inequals(TFinal other)
        {
            return !Equals(other);
        }

        #endregion

        #region IComparable<TFinal> Members

        public virtual int CompareTo(TFinal other)
        {
            if (this.LessThan(other))
                return -1;
            if (this.Equals(other))
                return 0;
            return 1;
        }

        #endregion

        #region ISign Members

        public virtual SignTypes Sign
        {
            get
            {
                if (IsNaN)
                    return SignTypes.NaN;
                if (IsPositiveInfinity)
                    return SignTypes.PositiveInfinity;
                if (IsNegativeInfinity)
                    return SignTypes.NegativeInfinity;
                if (IsZero)
                    return SignTypes.Zero;
                return LessThan(Zero) ? SignTypes.Negative : SignTypes.Positive;
            }
        }

        #endregion

        public TFinal GetAbs()
        {
            if(LessThan(Zero))
                return GetNegation();
            return Self;
        }

        public static TFinal Power<TInteger>(TFinal x, TInteger y)
            where TInteger : Integer<TInteger>, new()
        {
            if (x.Sign == SignTypes.Zero)
            {
                if (y.Sign == SignTypes.Zero)
                    throw new ArithmeticException();
                return x;
            }
            if (x.Equals(One))
            {
                return x;
            }
            bool neg = false;
            switch (y.Sign)
            {
                case SignTypes.Zero:
                    return One;
                case SignTypes.Negative:
                    neg = true;
                    y = y.GetNegation();
                    break;
            }
            TInteger t, two = Integer<TInteger>.One.Add(Integer<TInteger>.One);
            if (x.Equals(NegativeOne))
            {
                if (y.Modulus(two).Sign == SignTypes.Zero)
                    return One;
                return x;
            }
            TFinal xx = One;
            while (true)
            {
                y = y.DivRem(two, out t);
                if (t.Sign == SignTypes.Positive)
                    xx = xx.MultipliedBy(x);
                if (y.Sign != SignTypes.Positive)
                    break;
                x = x.MultipliedBy(x);
            }
            if (neg)
                xx = One.DividedBy(xx);
            return xx;
        }
    }
}
